# 高通QSDK iwinfo工具分析 ## 概述 iwinfo是基于IOCTRL的获取无线接口信息的命令 ## 基本命令 ```c fprintf(stderr, "Usage:\n" " iwinfo info\n" " iwinfo scan\n" " iwinfo txpowerlist\n" " iwinfo freqlist\n" " iwinfo assoclist\n" " iwinfo countrylist\n" " iwinfo htmodelist\n" " iwinfo phyname
\n" ); ``` ## 代码分析 1. main函数在iwinfo_cli.c中 ```c int main(int argc, char **argv) { const struct iwinfo_ops *iw; // 根据 athx 得到相应的iw ops 下面分析 iw = iwinfo_backend(argv[1]); for (i = 2; i < argc; i++){ switch(argv[i][0]) //argv[2][0] 比如 argv[2] = info 那么 argv[2][0]=i 执行case i { case 'i': print_info(iw, argv[1]); break; case 's': print_scanlist(iw, argv[1]); break; case 't': print_txpwrlist(iw, argv[1]); break; case 'f': print_freqlist(iw, argv[1]); break; case 'a': print_assoclist(iw, argv[1]); break; case 'c': print_countrylist(iw, argv[1]); break; case 'h': print_htmodelist(iw, argv[1]); break; default: fprintf(stderr, "Unknown command: %s\n", argv[i]); rv = 1; } } } ``` 2. 下面分析iwinfo_backend函数 ```c //iwinfo_lib.c const struct iwinfo_ops * iwinfo_backend(const char *ifname) { int i; for (i = 0; i < ARRAY_SIZE(backends); i++) if (backends[i]->probe(ifname)) // probe 返回1的时候说明找到对应的ops return backends[i]; // 这里的backends 是个全局变量,定义如下: /* static const struct iwinfo_ops *backends[] = { #ifdef USE_NL80211 &nl80211_ops, #endif #ifdef USE_MADWIFI &madwifi_ops, #endif #ifdef USE_QCAWIFI &qcawifi_ops, #endif #ifdef USE_WL &wl_ops, #endif &wext_ops, }; */ /* 假如ifname = ath2 下面会轮询调用backends[i]->probe(ath2),会调用ops->probe(ath2) 这里分析高通qcawifi_ops */ return NULL; } const struct iwinfo_ops qcawifi_ops = { .name = "qcawifi", .probe = qcawifi_probe, .channel = qcawifi_get_channel, .frequency = qcawifi_get_frequency, .frequency_offset = qcawifi_get_frequency_offset, .txpower = qcawifi_get_txpower, .txpower_offset = qcawifi_get_txpower_offset, .bitrate = qcawifi_get_bitrate, .signal = qcawifi_get_signal, .noise = qcawifi_get_noise, .quality = qcawifi_get_quality, .quality_max = qcawifi_get_quality_max, .mbssid_support = qcawifi_get_mbssid_support, .hwmodelist = qcawifi_get_hwmodelist, .mode = qcawifi_get_mode, .ssid = qcawifi_get_ssid, .bssid = qcawifi_get_bssid, .country = qcawifi_get_country, .hardware_id = qcawifi_get_hardware_id, .hardware_name = qcawifi_get_hardware_name, .encryption = qcawifi_get_encryption, .phyname = qcawifi_get_phyname, .assoclist = qcawifi_get_assoclist, .txpwrlist = qcawifi_get_txpwrlist, .scanlist = qcawifi_get_scanlist, .freqlist = qcawifi_get_freqlist, .countrylist = qcawifi_get_countrylist, .close = qcawifi_close }; int qcawifi_probe(const char *ifname) { return ( !!qcawifi_isvap(ifname, NULL) || qcawifi_iswifi(ifname) ); { // 这两个函数解释参见下面,如果此时输入的是ath2 这里qcawifi_isvap(ifname, NULL) 为wifi !!wifi 即为 1 // qcawifi_iswifi(ifname) 返回0 } } // 判断ifname是 vap 还是wifi ath2是vap wifi2 是wifi /* * cat /sys/class/net/ath2/parent * wifi2 * * ifname=ath2 返回wifi * ifname=wifi2 返回NULL */ static char * qcawifi_isvap(const char *ifname, const char *wifiname) { int fd, ln; char path[64]; char *ret = NULL; static char name[IFNAMSIZ]; if( strlen(ifname) <= 9 ) { sprintf(path, "/sys/class/net/%s/parent", ifname); //如果输入是wifi2 这里打开会失败 ret=NULL if( (fd = open(path, O_RDONLY)) > -1 ) { if( wifiname != NULL ) { // 这里一般不会进,忽略 if( read(fd, name, strlen(wifiname)) == strlen(wifiname) ) ret = strncmp(name, wifiname, strlen(wifiname)) ? NULL : name; } else if( (ln = read(fd, name, IFNAMSIZ)) >= 4 ) { // 如果输入的是ath2 这里的ret是wifi name[ln-1] = 0; ret = name; } (void) close(fd); } } return ret; } /* * cat /sys/class/net/wifi2/hwcaps * 80211an/ac/ax * * ifname=ath2 返回0 * ifname=wifi2 返回1 */ static int qcawifi_iswifi(const char *ifname) { int fd, ln; int ret = 0; char prot[16]; char path[64]; /* qcawifi has a "hwcaps" file in wifiN sysfs to define the * protocol actually supported by the hardware */ sprintf(path, "/sys/class/net/%s/hwcaps", ifname); if( (fd = open(path, O_RDONLY)) > -1 ) { if( ln = read(fd, prot, sizeof(prot))) ret = !strncmp(prot, "802.11", 6); //如果输入是wifi2 这里ret=1 close(fd); } return ret; } ``` 3. 下面以 iwinfo ath2 assoclist 为例说明 ```c //很明显 这里会调用到 // case 'a': // print_assoclist(iw, argv[1]); // break; static void print_assoclist(const struct iwinfo_ops *iw, const char *ifname) { int i, len; char buf[IWINFO_BUFSIZE]; struct iwinfo_assoclist_entry *e; // qcawifi_get_assoclist 函数会被调用 if (iw->assoclist(ifname, buf, &len)) { printf("No information available\n"); return; } else if (len <= 0) { printf("No station connected\n"); return; } for (i = 0; i < len; i += sizeof(struct iwinfo_assoclist_entry)) { e = (struct iwinfo_assoclist_entry *) &buf[i]; printf("%s %s / %s (SNR %d) %d ms ago\n", format_bssid(e->mac), format_signal(e->signal), format_noise(e->noise), (e->signal - e->noise), e->inactive); printf(" RX: %-38s %8d Pkts.\n", format_assocrate(&e->rx_rate), e->rx_packets ); printf(" TX: %-38s %8d Pkts.\n", format_assocrate(&e->tx_rate), e->tx_packets ); printf(" expected throughput: %s\n\n", format_rate(e->thr)); } } ``` 4. 下面分析qcawifi_get_assoclist函数 ```c int qcawifi_get_assoclist(const char *ifname, char *buf, int *len) { int bl, tl, noise; uint8_t *cp; uint8_t tmp[24*1024]; struct ieee80211req_sta_info *si; struct iwinfo_assoclist_entry entry; if( qcawifi_iswifi(ifname) ) return -1; if( (tl = get80211priv(ifname, IEEE80211_IOCTL_STA_INFO, tmp, 24*1024)) > 0 ) { cp = tmp; bl = 0; if( qcawifi_get_noise(ifname, &noise) ) noise = 0; do { si = (struct ieee80211req_sta_info *) cp; memset(&entry, 0, sizeof(entry)); entry.signal = (si->isi_rssi - 95); entry.noise = noise; memcpy(entry.mac, &si->isi_macaddr, 6); entry.inactive = si->isi_inact * 1000; entry.tx_packets = (si->isi_txseqs[0] & IEEE80211_SEQ_SEQ_MASK) >> IEEE80211_SEQ_SEQ_SHIFT; entry.rx_packets = (si->isi_rxseqs[0] & IEEE80211_SEQ_SEQ_MASK) >> IEEE80211_SEQ_SEQ_SHIFT; if(si->isi_txratekbps == 0) entry.tx_rate.rate = (si->isi_rates[si->isi_txrate] & IEEE80211_RATE_VAL)/2 * 1000; else entry.tx_rate.rate = si->isi_txratekbps; entry.rx_rate.rate = si->isi_rxratekbps; entry.rx_rate.mcs = -1; entry.tx_rate.mcs = -1; memcpy(&buf[bl], &entry, sizeof(struct iwinfo_assoclist_entry)); bl += sizeof(struct iwinfo_assoclist_entry); cp += si->isi_len; tl -= si->isi_len; } while (tl >= sizeof(struct ieee80211req_sta_info)); *len = bl; return 0; } return -1; } ``` 5. 下面分析get80211priv ```c static int get80211priv(const char *ifname, int op, void *data, size_t len) { struct iwreq iwr; if( qcawifi_wrq(&iwr, ifname, op, data, len) < 0 ) return -1; return iwr.u.data.length; } static int qcawifi_wrq(struct iwreq *wrq, const char *ifname, int cmd, void *data, size_t len) { strncpy(wrq->ifr_name, ifname, IFNAMSIZ); if( data != NULL ) { if( len < IFNAMSIZ ) { memcpy(wrq->u.name, data, len); } else { wrq->u.data.pointer = data; wrq->u.data.length = len; } } return iwinfo_ioctl(cmd, wrq); } //iwinfo_utils.c static int ioctl_socket = -1; struct uci_context *uci_ctx = NULL; static int iwinfo_ioctl_socket(void) { /* Prepare socket */ if (ioctl_socket == -1) { ioctl_socket = socket(AF_INET, SOCK_DGRAM, 0); fcntl(ioctl_socket, F_SETFD, fcntl(ioctl_socket, F_GETFD) | FD_CLOEXEC); } return ioctl_socket; } int iwinfo_ioctl(int cmd, void *ifr) { int s = iwinfo_ioctl_socket(); return ioctl(s, cmd, ifr); // ioctl 调用 } ``` 6. 到驱动层 ```c int ieee80211_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { switch (cmd) { case IEEE80211_IOCTL_STA_INFO: return ieee80211_ioctl_getstainfo(dev, (struct iwreq *) ifr); } } static int ieee80211_ioctl_getstainfo(struct net_device *dev, struct iwreq *iwr) { osif_dev *osifp = ath_netdev_priv(dev); wlan_if_t vap = osifp->os_if; int error = 0, status = 0; uint32_t data_length = 0; void *p; uint32_t memory_allocated; memory_allocated = iwr->u.data.length; data_length = ieee80211_ucfg_getstaspace(vap); if (data_length == 0) return -EPERM; if ((data_length > iwr->u.data.length) && (iwr->u.data.flags == 0)) { return data_length; } if((iwr->u.data.flags == 1) && (data_length > memory_allocated*LIST_STA_SPLIT_UNIT)) { return -EAGAIN; } p = (void *)OS_MALLOC(osifp->os_handle, data_length, GFP_KERNEL); if (p == NULL) return -ENOMEM; status = ieee80211_ucfg_getstainfo(vap, p, &data_length); if (data_length > 0) { iwr->u.data.length = data_length; error = _copy_to_user(iwr->u.data.pointer, p, data_length); status = error ? -EFAULT : 0; } else { iwr->u.data.length = 0; } OS_FREE(p); return status; } //qca/src/qca-wifi/umac/base/ieee80211_ucfg.c int ieee80211_ucfg_getstainfo(wlan_if_t vap, struct ieee80211req_sta_info *si, uint32_t *len) { struct stainforeq req; if (*len < sizeof(struct ieee80211req_sta_info)) return -EFAULT; /* estimate space required for station info */ req.space = sizeof(struct stainforeq); req.vap = vap; if (*len > 0) { size_t space = *len; if (si == NULL) return -ENOMEM; req.si = si; req.space = *len; wlan_iterate_station_list(vap, get_sta_info, &req); *len = space - req.space; } else *len = 0; return 0; } //qca/src/qca-wifi/umac/base/ieee80211_node.c int32_t wlan_iterate_station_list(wlan_if_t vap,ieee80211_sta_iter_func iter_func,void *arg) { return ieee80211_iterate_node_list(vap, iter_func, arg, IEEE80211_NODE_ITER_F_ASSOC_STA); } //qca/src/qca-wifi/umac/base/ieee80211_node.c static int32_t ieee80211_iterate_node_list(wlan_if_t vap,ieee80211_sta_iter_func iter_func,void *arg, u_int32_t flag) { struct ieee80211com *ic = vap->iv_ic; struct ieee80211_iter_arg *itr_arg = NULL; int i, count; itr_arg = (struct ieee80211_iter_arg *)qdf_mem_malloc(sizeof(struct ieee80211_iter_arg)); if (itr_arg == NULL) { return -1; } itr_arg->count=0; itr_arg->vap=vap; itr_arg->flag=flag; /* * we can not call the call back function iter_func from the ieee80211_sta_iter. * because the ieee80211_iter is called with nt lock held and will result in * dead lock if the implementation of iter_func calls bcak into umac to query more * info about the node (which is more likely). * instaed the ieee80211_sta_iter collects all the nodes in to the nodes array * part of the itr_arg and also increments the ref count on these nodes so that * they wont get freed. */ wlan_objmgr_iterate_peerobj_list(vap->vdev_obj, ieee80211_node_iter, (void *)itr_arg, WLAN_MLME_SB_ID); for (i = 0;i < itr_arg->count; ++i) { if (i == ic->ic_num_clients) break; if (iter_func) { /* * node has been refed in ieee80211_sta_iter * so safe to acces the contentes of the node. */ (* iter_func) (arg, itr_arg->nodes[i]); // iter_func=get_sta_info 下面分析该函数 } /* decrement the ref count which is incremented above in ieee80211_sta_iter */ ieee80211_free_node(itr_arg->nodes[i], WLAN_MLME_SB_ID); } count = itr_arg->count; qdf_mem_free(itr_arg); return (count); } void get_sta_info(void *arg, wlan_node_t node) { struct stainforeq *req = arg; wlan_if_t vap = req->vap; struct ieee80211req_sta_info *si; size_t ielen, len; u_int8_t *cp; u_int8_t ni_ie[IEEE80211_MAX_OPT_IE]; u_int16_t ni_ie_len = IEEE80211_MAX_OPT_IE; u_int8_t *macaddr = wlan_node_getmacaddr(node); wlan_snr_info snr_info; wlan_chan_t chan = wlan_node_get_chan(node); ieee80211_rate_info rinfo; u_int32_t jiffies_now=0, jiffies_delta=0, jiffies_assoc=0; struct wlan_objmgr_psoc *psoc; u_int32_t op_class=0, op_rates=0; ol_txrx_soc_handle soc_dp_handle; struct wlan_objmgr_pdev *pdev; QDF_STATUS status; cdp_peer_stats_param_t buf = {0}; /* already ignore invalid nodes in UMAC */ if (chan == IEEE80211_CHAN_ANYC) { /* XXX bogus entry */ return; } if (!vap || !vap->iv_ic) return; pdev = vap->iv_ic->ic_pdev_obj; psoc = wlan_pdev_get_psoc(pdev); len = sta_space(node, &ielen, vap); if (len > req->space) { return; } si = req->si; si->awake_time = node->awake_time; si->ps_time = node->ps_time; /* if node state is currently in power save when the wlanconfig command is given, add time from previous_ps_time until current time to power save time */ if(node->ps_state == 1) { si->ps_time += qdf_get_system_timestamp() - node->previous_ps_time; } /* if node state is currently in active state when the wlanconfig command is given, add time from previous_ps_time until current time to awake time */ else if(node->ps_state == 0) { si->awake_time += qdf_get_system_timestamp() - node->previous_ps_time; } si->isi_assoc_time = wlan_node_get_assocuptime(node); jiffies_assoc = wlan_node_get_assocuptime(node); /* Jiffies to timespec conversion for si->isi_tr069_assoc_time */ jiffies_now = OS_GET_TICKS(); jiffies_delta = jiffies_now - jiffies_assoc; jiffies_to_timespec(jiffies_delta, &si->isi_tr069_assoc_time); si->isi_len = len; si->isi_ie_len = ielen; si->isi_freq = wlan_channel_frequency(chan); si->isi_band = reg_wifi_band_to_wlan_band_id(wlan_reg_freq_to_band(si->isi_freq)); if(!vap->iv_ic->ic_is_target_lithium){ qdf_err("ic_is_target_lithium if null"); return; } if(vap->iv_ic->ic_is_target_lithium(psoc)){ if(!vap->iv_ic->ic_get_cur_hw_nf){ qdf_err("ic_get_cur_hw_nf is null"); return; } si->isi_nf = vap->iv_ic->ic_get_cur_hw_nf(vap->iv_ic); } else { if(!vap->iv_ic->ic_get_cur_chan_nf){ qdf_err("ic_get_cur_hw_nf is null"); return; } si->isi_nf = vap->iv_ic->ic_get_cur_chan_nf(vap->iv_ic); } si->isi_ieee = wlan_channel_ieee(chan); si->isi_flags = wlan_channel_flags(chan); si->isi_state = wlan_node_get_state_flag(node); si->isi_ps = node->ps_state; si->isi_authmode = wlan_node_get_authmode(node); if (wlan_node_getsnr(node, &snr_info, WLAN_SNR_RX) == 0) { si->isi_rssi = snr_info.avg_snr; si->isi_min_rssi = node->ni_snr_min; si->isi_max_rssi = node->ni_snr_max; } si->isi_capinfo = wlan_node_getcapinfo(node); #if ATH_BAND_STEERING si->isi_pwrcapinfo = wlan_node_getpwrcapinfo(node); #endif #if UMAC_SUPPORT_RRM OS_MEMCPY(si->isi_rrm_caps, node->ni_rrm_caps, sizeof(si->isi_rrm_caps)); #endif si->isi_athflags = wlan_node_get_ath_flags(node); si->isi_erp = wlan_node_get_erp(node); si->isi_operating_bands = wlan_node_get_operating_bands(node); si->isi_beacon_measurement_support = wlan_node_has_extflag(node, IEEE80211_NODE_BCN_MEASURE_SUPPORT); IEEE80211_ADDR_COPY(si->isi_macaddr, macaddr); if (wlan_node_txrate_info(node, &rinfo) == 0) { si->isi_txratekbps = rinfo.rate; si->isi_maxrate_per_client = rinfo.maxrate_per_client; #if ATH_EXTRA_RATE_INFO_STA si->isi_tx_rate_mcs = rinfo.mcs; si->isi_tx_rate_flags = rinfo.flags; #endif } /* supported operating classes */ if (node->ni_supp_op_class_ie != NULL) { si->isi_curr_op_class = node->ni_supp_op_cl.curr_op_class; si->isi_num_of_supp_class = node->ni_supp_op_cl.num_of_supp_class; for(op_class = 0; op_class < node->ni_supp_op_cl.num_of_supp_class && op_class < MAX_NUM_OPCLASS_SUPPORTED; op_class++) { si->isi_supp_class[op_class] = node->ni_supp_op_cl.supp_class[op_class]; } } else { si->isi_num_of_supp_class = 0; } /* supported channels */ if (node->ni_supp_chan_ie != NULL) { si->isi_first_channel = node->ni_first_channel; si->isi_nr_channels = node->ni_nr_channels; } else { si->isi_nr_channels = 0; } /* supported rates */ for (op_rates = 0;op_rates < node->ni_rates.rs_nrates;op_rates++) { si->isi_rates[op_rates] = node->ni_rates.rs_rates[op_rates]; } memset(&rinfo, 0, sizeof(rinfo)); if (wlan_node_rxrate_info(node, &rinfo) == 0) { si->isi_rxratekbps = rinfo.rate; #if ATH_EXTRA_RATE_INFO_STA si->isi_rx_rate_mcs = rinfo.mcs; si->isi_rx_rate_flags = rinfo.flags; #endif } si->isi_associd = wlan_node_get_associd(node); si->isi_txpower = wlan_node_get_txpower(node); si->isi_vlan = wlan_node_get_vlan(node); si->isi_cipher = IEEE80211_CIPHER_NONE; if (wlan_get_param(vap, IEEE80211_FEATURE_PRIVACY)) { do { ieee80211_cipher_type uciphers[1]; int count = 0; count = wlan_node_get_ucast_ciphers(node, uciphers, 1); if (count == 1) { si->isi_cipher |= 1<isi_txseqs, sizeof(si->isi_txseqs)); wlan_node_get_rxseqs(node, si->isi_rxseqs, sizeof(si->isi_rxseqs)); si->isi_uapsd = wlan_node_get_uapsd(node); si->isi_opmode = IEEE80211_STA_OPMODE_NORMAL; psoc = wlan_pdev_get_psoc(pdev); soc_dp_handle = wlan_psoc_get_dp_handle(psoc); if (!soc_dp_handle) return; status = cdp_txrx_get_peer_stats_param(soc_dp_handle, wlan_vdev_get_id(node->peer_obj->peer_objmgr.vdev), node->peer_obj->macaddr, cdp_peer_tx_inactive_time, &buf); if (QDF_IS_STATUS_ERROR(status)) return; si->isi_inact = buf.tx_inactive_time; /* 11n */ si->isi_htcap = wlan_node_get_htcap(node); si->isi_stamode= wlan_node_get_mode(node); si->isi_curr_mode = get_phymode_from_chwidth(vap->iv_ic, node); #if ATH_SUPPORT_EXT_STAT si->isi_vhtcap = wlan_node_get_vhtcap(node); si->isi_chwidth = (u_int8_t) wlan_node_get_chwidth(node); #endif /* Extended capabilities */ si->isi_ext_cap = wlan_node_get_extended_capabilities(node); si->isi_ext_cap2 = wlan_node_get_extended_capabilities2(node); si->isi_ext_cap3 = wlan_node_get_extended_capabilities3(node); si->isi_ext_cap4 = wlan_node_get_extended_capabilities4(node); si->isi_nss = wlan_node_get_nss(node); si->isi_supp_nss = wlan_node_get_nss_capability(node); si->isi_is_256qam = wlan_node_get_256qam_support(node); si->isi_is_he = !!(IEEE80211_NODE_USE_HE(node)); if (si->isi_is_he) { qdf_mem_copy(&si->isi_hecap_rxmcsnssmap, &node->ni_he.hecap_rxmcsnssmap, sizeof(u_int16_t) * HEHANDLE_CAP_TXRX_MCS_NSS_SIZE); qdf_mem_copy(&si->isi_hecap_txmcsnssmap, &node->ni_he.hecap_txmcsnssmap, sizeof(u_int16_t) * HEHANDLE_CAP_TXRX_MCS_NSS_SIZE); qdf_mem_copy(&si->isi_hecap_phyinfo, &node->ni_he.hecap_phyinfo, sizeof(u_int32_t) * HEHANDLE_CAP_PHYINFO_SIZE); } cp = (u_int8_t *)(si+1); if(!wlan_node_getwpaie(vap, macaddr, ni_ie, &ni_ie_len)) { OS_MEMCPY(cp, ni_ie, ni_ie_len); cp += ni_ie_len; ni_ie_len = IEEE80211_MAX_OPT_IE; } if(!wlan_node_getwmeie(vap, macaddr, ni_ie, &ni_ie_len)) { OS_MEMCPY(cp, ni_ie, ni_ie_len); cp += ni_ie_len; ni_ie_len = IEEE80211_MAX_OPT_IE; } if(!wlan_node_getathie(vap, macaddr, ni_ie, &ni_ie_len)) { OS_MEMCPY(cp, ni_ie, ni_ie_len); cp += ni_ie_len; ni_ie_len = IEEE80211_MAX_OPT_IE; } if(!wlan_node_getwpsie(vap, macaddr, ni_ie, &ni_ie_len)) { OS_MEMCPY(cp, ni_ie, ni_ie_len); cp += ni_ie_len; ni_ie_len = IEEE80211_MAX_OPT_IE; } if (!wlan_node_get_suppchanie(vap, macaddr, ni_ie, &ni_ie_len)) { OS_MEMCPY(cp, ni_ie, ni_ie_len); cp += ni_ie_len; ni_ie_len = IEEE80211_MAX_OPT_IE; } if (!wlan_node_get_opclassie(vap, macaddr, ni_ie, &ni_ie_len)) { OS_MEMCPY(cp, ni_ie, ni_ie_len); cp += ni_ie_len; ni_ie_len = IEEE80211_MAX_OPT_IE; } req->si = ( struct ieee80211req_sta_info *)(((u_int8_t *)si) + len); req->space -= len; } ```